package com.yztstudy.Service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yztstudy.Mapper.PictureMapper;
import com.yztstudy.Mapper.ProductMapper;
import com.yztstudy.Service.ProductService;
import com.yztstudy.clients.*;
import com.yztstudy.entity.Picture;
import com.yztstudy.entity.Product;
import com.yztstudy.param.*;
import com.yztstudy.to.OrderToProduct;
import com.yztstudy.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ProductServiceImpl implements ProductService {

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private PictureMapper pictureMapper;

    @Resource
    private CategoryFeignClient categoryFeignClient; // 远程调用类别服务

    @Resource
    private SearchFeignClient searchFeignClient; // 远程调用搜索服务

    @Resource
    private RabbitTemplate rabbitTemplate; // RabbitMQ的操作模板

    @Resource
    private CartFeignClient cartFeignClient; // 远程调用购物车服务

    @Resource
    private OrderFeignClient orderFeignClient; // 远程调用订单服务

    @Resource
    private CollectFeignClient collectFeignClient; // 远程调用收藏服务

    /**
     * TODO 查询类别的主键
     * @param productPromoParam 前端发过来的类别名称
     * @return
     */
    @Cacheable(value = "list.product", key = "#productPromoParam.categoryName", cacheManager = "cacheManagerHour")
    @Override
    public R promo(ProductPromoParam productPromoParam) {

        // TODO 1.开始查询商品的主键
        R result = categoryFeignClient.byName(productPromoParam.getCategoryName());

        // TODO 2.判断是否成功
        if (result.getCode().equals(R.FAIL_CODE)) // 失败
        {
            log.info("ProductServiceImpl.promo业务结束, 结果:{}", "类别查询失败!");
            return result;
        }

        // TODO 3.查询成功
        LinkedHashMap<String, Object> category = (LinkedHashMap<String, Object>) result.getData();
        QueryWrapper<Product> wrapper = new QueryWrapper<>();
        wrapper.eq("category_id", category.get("category_id"));
        wrapper.orderByDesc("product_sales"); // 根据销量倒序

        IPage<Product> page = new Page<>(1, 7); // 分页查询
        List<Product> products = productMapper.selectPage(page, wrapper).getRecords();

        log.info("ProductServiceImpl.promo业务结束, 结果:{}", "数据查询成功!");
        return R.ok("数据查询成功!", products);
    }


    /**
     * TODO 多类别的热门商品查询
     * @param productHotParam 前端传过来的热门商品名称
     * @return
     */
    @Cacheable(value = "list.product", key = "#productHotParam.categoryName", cacheManager = "cacheManagerHour")
    @Override
    public R hots(ProductHotParam productHotParam) {

        // TODO 1.远程调用类别服务
        R result = categoryFeignClient.hots(productHotParam);

        // TODO 2.判断是否成功
        if (result.getCode().equals(R.FAIL_CODE)) // 失败
        {
            log.info("ProductServiceImpl.hots业务结束, 结果:{}", "多类别数据查询失败!");
            return result;
        }

        // TODO 3.查询成功
        List<Object> ids = (List<Object>) result.getData();
        QueryWrapper<Product> wrapper = new QueryWrapper<>();
        wrapper.in("category_id", ids);
        wrapper.orderByDesc("product_sales"); // 根据销量倒序

        IPage<Product> page = new Page<>(1, 7); // 分页查询
        List<Product> products = productMapper.selectPage(page, wrapper).getRecords();

        log.info("ProductServiceImpl.hots业务结束, 结果:{}", "多类别数据查询成功!");
        return R.ok("多类别数据查询成功!", products);
    }


    /**
     * TODO 查询全部的商品类别
     * @return
     */
    @Override
    public R list() {

        // TODO 1.调用远程类别服务
        R list = categoryFeignClient.list();

        // TODO 2.判断是否成功
        if (list.getCode().equals(R.FAIL_CODE)) // 失败
        {
            log.info("ProductServiceImpl.list业务结束, 结果:{}", "查询全部的商品类别失败!");
            return list;
        }

        // TODO 3.查询成功
        log.info("ProductServiceImpl.list业务结束, 结果:{}", "查询全部的商品类别成功!");
        return list;
    }


    /**
     * TODO 指定商品类别的分页查询
     * @param productIdsParam 前端传过来的参数(id数组，当前页面和每一页的大小)
     * @return
     */
    @Cacheable(value = "list.product", key = "#productIdsParam.categoryID + '-' + #productIdsParam.currentPage + '-' + #productIdsParam.pageSize", cacheManager = "cacheManagerHour")
    @Override
    public R byCategory(ProductIdsParam productIdsParam) {

        // TODO 1.判断id数组的长度是否为0
        List<Integer> categoryID = productIdsParam.getCategoryID();
        QueryWrapper<Product> wrapper = new QueryWrapper<>();
        if (!categoryID.isEmpty()) // 不为空
        {
            wrapper.in("category_id", categoryID);
        }
        IPage<Product> page = new Page<>(productIdsParam.getCurrentPage(), productIdsParam.getPageSize());

        // TODO 2.如果为空,查询全部
        List<Product> products = productMapper.selectPage(page, wrapper).getRecords();

        // TODO 3.结果集封装
        log.info("ProductServiceImpl.byCategory业务结束, 结果:{}", "指定商品类别的分页查询成功!");
        return R.ok("指定商品类别的分页查询成功!", products, page.getTotal());
    }


    /**
     * TODO 查询详情
     * @param productIdParam 商品的ID
     * @return
     */
    @Cacheable(value = "product", key = "#productIdParam", cacheManager = "cacheManagerHour")
    @Override
    public R detail(ProductIdParam productIdParam) {

        // TODO 1.查询数据库
        Product product = productMapper.selectById(productIdParam.getProductID());

        // TODO 2.判断这个对象是否为null
        if (product == null)
        {
            log.info("ProductServiceImpl.detail业务结束, 结果:{}", "指定商品的详情查询失败!");
            return R.fail("指定商品的详情查询失败!");
        }

        // TODO 3.如果对象不为null
        log.info("ProductServiceImpl.detail业务结束, 结果:{}", "指定商品的详情查询成功!");
        return R.ok("指定商品的详情查询成功!", product);
    }


    /**
     * TODO 商品的图片查询
     * @param productIdParam 商品的ID
     * @return
     */
    @Cacheable(value = "picture", key = "#productIdParam", cacheManager = "cacheManagerHour")
    @Override
    public R pictures(ProductIdParam productIdParam) {

        // TODO 1.查询数据库
        QueryWrapper<Picture> wrapper = new QueryWrapper<>();
        wrapper.eq("product_id", productIdParam.getProductID());
        List<Picture> pictures = pictureMapper.selectList(wrapper); // 一个商品，n张图片

        // TODO 2.判断查询是否成功
        if (pictures == null)
        {
            log.info("ProductServiceImpl.pictures业务结束, 结果:{}", "指定商品的图片查询失败!");
            return R.fail("指定商品的图片查询失败!");
        }

        // TODO 3.查询成功
        log.info("ProductServiceImpl.pictures业务结束, 结果:{}", "指定商品的图片查询成功!");
        return R.ok("指定商品的图片查询成功!", pictures);
    }


    /**
     * TODO 搜索服务,获取全部商品数据,用于同步到ES服务
     * @return
     */
    @Override
    public List<Product> allList() {

        // TODO 1.直接查询数据库
        List<Product> products = productMapper.selectList(null);
        log.info("ProductServiceImpl.allList业务结束, 结果:{}", "搜索服务,获取全部商品数据成功!");
        return products;
    }


    /**
     * TODO 搜索功能
     * @param productSearchParam 前端发来的搜索参数
     * @return
     */
    @Override
    public R search(ProductSearchParam productSearchParam) {

        // TODO 1.调用远程调用方法
        R result = searchFeignClient.searchProduct(productSearchParam);
        log.info("ProductServiceImpl.search业务结束, 结果:{}", "定向搜索服务,获取商品数据成功!");
        return result;
    }


    /**
     * TODO 根据商品ID集合查询多个商品
     * @param productCollectParam 商品ID的集合
     * @return
     */
    @Cacheable(value = "list.product",key="#productCollectParam.productIds",cacheManager = "cacheManagerDay")
    @Override
    public R productIds(ProductCollectParam productCollectParam) {

        // TODO 1.定义查询条件
        QueryWrapper<Product> wrapper = new QueryWrapper<>();
        wrapper.in("product_id", productCollectParam.getProductIds());

        // TODO 2.查询
        List<Product> products = productMapper.selectList(wrapper);

        // TODO 3.返回结果
        log.info("ProductServiceImpl.productIds业务结束, 结果:{}", "商品信息查询成功!");
        return R.ok("商品信息查询成功!", products);
    }


    /**
     * TODO 根据商品ID集合查询多个商品
     * @param productCollectParam 商品ID的集合
     * @return
     */
    @Override
    public List<Product> cartList(ProductCollectParam productCollectParam) {

        // TODO 1.定义查询条件
        QueryWrapper<Product> wrapper = new QueryWrapper<>();
        wrapper.in("product_id", productCollectParam.getProductIds());

        // TODO 2.查询
        List<Product> products = productMapper.selectList(wrapper);

        // TODO 3.返回结果
        log.info("ProductServiceImpl.cartList业务结束, 结果:{}", "商品信息查询成功!");
        return products;
    }


    /**
     * TODO 监听扣减库存，增加销量
     * @param orderToProducts 商品主键+数量的集合
     */
    @Transactional
    @Override
    public void subNumber(List<OrderToProduct> orderToProducts) {

        // TODO 1.将集合转换成Map
        Map<Integer, OrderToProduct> map = orderToProducts.stream().collect(Collectors.toMap(OrderToProduct::getProductId, v -> v));

        // TODO 2.获取商品的ID集合
        Set<Integer> productIds = map.keySet();

        // TODO 3.查询集合对应的商品信息
        List<Product> products = productMapper.selectBatchIds(productIds);

        // TODO 4.修改商品信息+更新
        for (Product product : products)
        {
            Integer num = map.get(product.getProductId()).getNum();
            product.setProductNum(product.getProductNum() - num); // 减库存
            product.setProductSales(product.getProductSales() + num); // 加销量
            productMapper.updateById(product); // 更新
        }

        log.info("ProductServiceImpl.subNumber业务结束, 结果:{}", "商品批量更新成功!");
    }


    /**
     * TODO 查询某个类别是否有商品
     * @param categoryId 商品ID
     * @return
     */
    @Override
    public Long adminCount(Integer categoryId) {

        // TODO 1.构造查询条件
        QueryWrapper<Product> wrapper = new QueryWrapper<>();
        wrapper.eq("category_id", categoryId);

        // TODO 2.查询
        Long count = productMapper.selectCount(wrapper);

        // TODO 3.返回结果
        log.info("ProductServiceImpl.adminCount业务结束, 结果:{}", count);
        return count;
    }


    /**
     * TODO 保存商品
     * @param productSaveParam 要保存的商品
     * @return
     */
    @Transactional
    @CacheEvict(value = "list.product", allEntries = true) // 清空缓存
    @Override
    public R adminSave(ProductSaveParam productSaveParam) {

        // TODO 1.先把新增的商品保存到数据库里面
        Product product = new Product();
        BeanUtils.copyProperties(productSaveParam, product); // 类型转换
        productMapper.insert(product); // 保存

        // TODO 2.商品的详情图片分割和保存
        String pictures = productSaveParam.getPictures();
        if (!StringUtils.isEmpty(pictures)) // 如果字符串不为null或者空字符串
        {
            String[] strings = pictures.split("\\+"); // 每一张详情的地址
            for (String url : strings)
            {
                Picture picture = new Picture();
                picture.setIntro(null);
                picture.setProductId(product.getProductId());
                picture.setProductPicture(url);
                pictureMapper.insert(picture); // 保存
            }
        }

        // TODO 3.同步到ES服务器中
        rabbitTemplate.convertAndSend("topic.ex", "product.insert", product);
        return R.ok("商品数据同步成功!");
    }


    /**
     * TODO 更新商品
     * @param product 商品
     * @return
     */
    @Transactional
    @Caching(evict = {@CacheEvict(value = "list.product", allEntries = true), @CacheEvict(value = "product", key = "#product.productId")}) // 删除缓存
    @CachePut(value = "product", key = "#product.productId") // 新增缓存
    @Override
    public R adminUpdate(Product product) {

        // TODO 1.直接更新
        int rows = productMapper.updateById(product);

        // TODO 2.判断返回值
        if (rows == 0)
        {
            return R.fail("商品更新失败!");
        }

        // TODO 3.更新成功,覆盖ES
        rabbitTemplate.convertAndSend("topic.ex", "product.insert", product);
        return R.ok("商品更新成功!");
    }


    /**
     * TODO 删除商品
     * @param productId 商品Id
     * @return
     */
    @Transactional
    @Caching(evict = {@CacheEvict(value = "list.product", allEntries = true), @CacheEvict(value = "product", key = "#productId")}) // 删除缓存
    @Override
    public R adminRemove(Integer productId) {

        // TODO 1.检查购物车是否存在
        R checkByCart = cartFeignClient.check(productId);
        if (checkByCart.getCode().equals("004")) // 存在
        {
            return checkByCart;
        }

        // TODO 2.查询订单是否存在
        R checkByOrder = orderFeignClient.check(productId);
        if (checkByOrder.getCode().equals("004")) // 存在
        {
            return checkByOrder;
        }

        // TODO 3.删除商品详情
        QueryWrapper<Picture> wrapper = new QueryWrapper<>();
        wrapper.eq("product_id", productId);
        pictureMapper.delete(wrapper);

        // TODO 4.删除收藏品中的商品
        collectFeignClient.removeByProductId(productId);


        // TODO 5.删除商品
        int rows = productMapper.deleteById(productId);
        if (rows == 0)
        {
            return R.fail("商品删除失败!");
        }

        // TODO 6.删除ES中的对应数据
        rabbitTemplate.convertAndSend("topic.ex", "product.remove", productId);

        // TODO 7.返回结果
        return R.ok("商品删除成功!");
    }
}
